package br.unb.graphics.qos;

import java.io.File;
import java.util.Iterator;

import br.unb.core.manager.Manager;
import br.unb.graphics.qos.charts.GenericQosChart;
import br.unb.graphics.qos.charts.JitterChart;
import br.unb.graphics.qos.charts.ThroughputChart;
import br.unb.graphics.qos.charts.LossChart;
import br.unb.graphics.qos.charts.DelayChart;

public class QoSController implements Runnable {

    private static QoSController INSTANCE = new QoSController();

    public static QoSController getInstance() {
        return INSTANCE;
    }
    boolean updateGraph = true;
    /** Default window size in miliseconds */
    private long windowSize = 2000;
    /**
     * Upper limit of the window must be equal to the 
     * largest output time of all incomming packets
     */
    private long windowUpperBound = 0;
    Thread loadValues;
    private boolean isRunning;
    //HashMap qosTabs = new HashMap();
    DelayChart dc = new DelayChart();
    JitterChart jc = new JitterChart();
    LossChart lc = new LossChart(windowSize);
    ThroughputChart bc = new ThroughputChart(windowSize);
    static final String DELAY = "Delay";
    static final String JITTER = "Jitter";
    static final String LOSS = "Loss";
    static final String THROUGHPUT = "Throughput";

    private QoSController() {
        this.addAllTrafficToChart(dc);
        this.addAllTrafficToChart(jc);
        this.addAllTrafficToChart(lc);
        this.addAllTrafficToChart(bc);
    }

    private synchronized void addAllTrafficToChart(GenericQosChart qosControl) {
        Iterator iterator = Manager.getTrafficMap().keySet().iterator();
        while (iterator.hasNext()) {
            qosControl.addTrafficToQos((String) iterator.next());
        }
    }

    public synchronized QosChart loadDelayChart() {
        this.addAllTrafficToChart(dc);
        return dc;
    }

    public synchronized QosChart loadJitterChart() {
        this.addAllTrafficToChart(jc);
        return jc;
    }

    public synchronized QosChart loadLossChart() {
        this.addAllTrafficToChart(lc);
        return lc;
    }

    public synchronized QosChart loadBandwidthChart() {
        this.addAllTrafficToChart(bc);
        return bc;
    }

    public synchronized void removeDelayChart() {
        //qosTabs.remove(DELAY);
    }

    public synchronized void removeJitterChart() {
        //qosTabs.remove(JITTER);
    }

    public synchronized void removeLossChart() {
        //qosTabs.remove(LOSS);
    }

    public synchronized void removeBandwidthChart() {
        //qosTabs.remove(THROUGHPUT);
    }

    /**
     * Adds a traffic to the graphics
     * @param String TrafficName name of the traffic to be added, must be unique
     */
    public synchronized void addTrafficToQos(String TrafficName) {
        bc.addTrafficToQos(TrafficName);
        lc.addTrafficToQos(TrafficName);
        dc.addTrafficToQos(TrafficName);
        jc.addTrafficToQos(TrafficName);

    }

    /**
     * Removes a traffic from the graphics
     * @param String TrafficName name of the traffic to be removed, must be unique
     */
    public synchronized void removeTrafficFromQos(String trafficName) {
        bc.removeTrafficFromQos(trafficName);
        dc.removeTrafficFromQos(trafficName);
        jc.removeTrafficFromQos(trafficName);
        lc.removeTrafficFromQos(trafficName);
    }

    /**
     * Adds values of qos parameters to vectors
     * NrPacket	LenPacket	SendTime	ReceiveTime
     *
     * if the packet is not inside the window it is discarted
     *           {____windowSize___}
     * ---------+------------------+-------------
     *                           windowUpperBound
     */
    public synchronized void addQosToTraffic(
            int nrPacket,
            int lenPacket,
            long sendTime,
            long receiveTime,
            String trafficName) {
        /*if inside valid interval put it inside time window*/
        if (sendTime >= (windowUpperBound - 1000 * windowSize)) {
            if (sendTime > windowUpperBound) {
                windowUpperBound = (long) sendTime;
            }
            bc.addQosToTraffic(nrPacket, lenPacket, sendTime, receiveTime, trafficName);
            dc.addQosToTraffic(nrPacket, lenPacket, sendTime, receiveTime, trafficName);
            jc.addQosToTraffic(nrPacket, lenPacket, sendTime, receiveTime, trafficName);
            lc.addQosToTraffic(nrPacket, lenPacket, sendTime, receiveTime, trafficName);
            //TODO update graphics online
        }
    }

    /*Adds values from vectors to graphics*/
    private synchronized void addValuesToCharts() {
        //aqController.addValuesToCharts();
        bc.addValuesToCharts();
        dc.addValuesToCharts();
        jc.addValuesToCharts();
        lc.addValuesToCharts();
    }

    /**
     * Runs the values from a file to the graphics
     * @param file 
     */
    public void loadValuesFromFile(File file) {
        //aqController.loadValuesFromFile(file);
        bc.loadValuesFromFile(file);
        dc.loadValuesFromFile(file);
        jc.loadValuesFromFile(file);
        lc.loadValuesFromFile(file);
    }

    public void play() {
        if (this.isRunning) {
            return;
        }
        this.isRunning = true;
        this.loadValues = new Thread(this, "QoSController");
        loadValues.start();
    }

    public void stop() {
        this.isRunning = false;
        //aqController.restart();
        bc.restart();
        dc.restart();
        jc.restart();
        lc.restart();
    }

    public void run() {
        this.loadValues.setPriority(Thread.NORM_PRIORITY + 1);
        while (isRunning) {
            try {
                Thread.sleep(windowSize);//Loads the graphics each 50 ms
            } catch (InterruptedException ie) {
            }
            this.addValuesToCharts();
        }
    }

    public long getWindowSize() {
        return windowSize;
    }

    public void setWindowSize(long wSize) {
        windowSize = wSize;
        //aqController.setWindowSize(wSize);
        bc.setWindowSize(wSize);
        lc.setWindowSize(wSize);
    }
}
